#include "simulator.h"
#include "kernel.h"
#include "ui_simulator.h"
#include "workthread.h"
#include <QDebug>
#include <QFileDialog>
#include <QMessageBox>
#include <QString>
#include <cstdint>
#include <sstream>
#include <string>
using namespace std;

extern void InitAssembler();
extern void InitAntiAssembler();
extern string Asm2Bin(const string &);
extern string Bin2Asm(const string &);

Simulator::Simulator(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::Simulator), workthread(new WorkThread)
{
  InitAssembler();
  InitAntiAssembler();

  ui->setupUi(this);

  ui->output->setReadOnly(true);
  ui->input->setReadOnly(true);
  ui->input->setPlaceholderText("当前不可输入");

  ui->registers->setEditTriggers(QAbstractItemView::NoEditTriggers);
  ui->registers->horizontalHeader()->setVisible(false);
  ui->registers->verticalHeader()->setVisible(false);
  ui->registers->setSelectionBehavior(QAbstractItemView::SelectRows);
  for (int i = 0; i < 8; ++i)
    ui->registers->setItem(i, 0, new QTableWidgetItem(QString("R%1").arg(i)));
  ui->registers->setItem(8, 0, new QTableWidgetItem("IR"));
  ui->registers->setItem(9, 0, new QTableWidgetItem("当前行"));
  ui->registers->setItem(10, 0, new QTableWidgetItem("溢出位"));
  ui->registers->setItem(11, 0, new QTableWidgetItem("比较位"));
  ui->registers->setItem(12, 0, new QTableWidgetItem("栈顶"));
  ui->registers->setColumnWidth(0, 60);
  ui->registers->setColumnWidth(1, 190);
  updateData();

  connect(ui->run, SIGNAL(clicked()), workthread, SLOT(start()));
  connect(workthread, SIGNAL(update()), this, SLOT(updateData()));
  connect(workthread, SIGNAL(output()), this, SLOT(output()),
          Qt::DirectConnection);
  connect(workthread, SIGNAL(input()), this, SLOT(input()),
          Qt::DirectConnection);
  connect(workthread, SIGNAL(quit()), this, SLOT(over()));
  connect(ui->savea, SIGNAL(clicked()), this, SLOT(savea()));
  connect(ui->saveb, SIGNAL(clicked()), this, SLOT(saveb()));
  connect(ui->loada, SIGNAL(clicked()), this, SLOT(loada()));
  connect(ui->loadb, SIGNAL(clicked()), this, SLOT(loadb()));
  connect(ui->asm2bin, SIGNAL(clicked()), this, SLOT(asm2bin()));
  connect(ui->bin2asm, SIGNAL(clicked()), this, SLOT(bin2asm()));
}

Simulator::~Simulator() { delete ui; }

void Simulator::updateData()
{
  using simu_kernel::GR;
  using simu_kernel::IR;
  using simu_kernel::pos;
  using simu_kernel::state;
  using simu_kernel::x;

  // 寄存器与状态字
  for (int i = 0; i < 8; ++i)
    ui->registers->setItem(i, 1,
                           new QTableWidgetItem(QString("%1").arg(GR[i])));
  ui->registers->setItem(
      8, 1,
      new QTableWidgetItem(QString("0x%1").arg(IR, 8, 16, QLatin1Char('0'))));
  ui->registers->setItem(9, 1, new QTableWidgetItem(QString("%1").arg(pos)));
  ui->registers->setItem(
      10, 1, new QTableWidgetItem(QString("%1").arg(state.overflow)));
  ui->registers->setItem(
      11, 1, new QTableWidgetItem(QString("%1").arg(state.compare)));
  ui->registers->setItem(12, 1, new QTableWidgetItem(QString("%1").arg(x)));

  // 内存区
  ui->memory->clear();
  uint8_t *tmp_data = simu_kernel::data;
  QString memory;
  for (uint i = 0; i < 60; ++i)
  {
    memory += QString("%1 %2 %3 %4\n")
                  .arg(tmp_data[i * 4 + 0], 8, 2, QLatin1Char('0'))
                  .arg(tmp_data[i * 4 + 1], 8, 2, QLatin1Char('0'))
                  .arg(tmp_data[i * 4 + 2], 8, 2, QLatin1Char('0'))
                  .arg(tmp_data[i * 4 + 3], 8, 2, QLatin1Char('0'));
  }
  ui->memory->append(memory);
}

void Simulator::savea()
{
  QString filename = QFileDialog::getSaveFileName();
  QFile asmFile(filename);
  asmFile.open(QFile::WriteOnly);
  QString fileBuffer = ui->text_assemble->toPlainText();
  asmFile.write(fileBuffer.toLocal8Bit());
  asmFile.close();
}

void Simulator::saveb()
{
  QString filename = QFileDialog::getSaveFileName();
  QFile binFile(filename);
  binFile.open(QFile::WriteOnly);
  QString BinIns = ui->text_binary->toPlainText();
  QDataStream out(&binFile);
  if (BinIns.at(1) == 'x')
  {
    QStringList Bins = BinIns.split("0x");
    for (auto it = Bins.begin() + 1; it != Bins.end(); ++it)
    {
      out << it->toUInt(nullptr, 16);
    }
  }
  else if (BinIns.at(1) == 'b')
  {
    QStringList Bins = BinIns.split("0b");
    for (auto it = Bins.begin() + 1; it != Bins.end(); ++it)
    {
      out << it->toUInt(nullptr, 2);
    }
  }
  binFile.close();
}

void Simulator::loada()
{
  QString filename = QFileDialog::getOpenFileName();
  QFile asmFile(filename);
  asmFile.open(QFile::ReadOnly);
  QByteArray fileBuffer = asmFile.readAll();
  ui->text_assemble->append(fileBuffer);
  asmFile.close();
}

void Simulator::loadb()
{
  QString filename = QFileDialog::getOpenFileName();
  QFile binFile(filename);
  binFile.open(QFile::ReadOnly);
  QDataStream in(&binFile);
  quint32 tmp, i = 0;
  ui->text_binary->clear();
  while (!in.atEnd())
  {
    in >> tmp;
    simu_kernel::op[i] = tmp;
    ++i;
    ui->text_binary->append(QString("0x%1").arg(tmp, 8, 16, QLatin1Char('0')));
  }
  simu_kernel::pc = simu_kernel::op;
  binFile.close();
}

void Simulator::asm2bin()
{
  try
  {
    stringstream bins(Asm2Bin(ui->text_assemble->toPlainText().toStdString()));
    string line;
    uint32_t i = 0;
    ui->text_binary->clear();
    while (getline(bins, line))
    {
      uint32_t tmp = stoi(line, nullptr, 2);
      simu_kernel::op[i] = tmp;
      ++i;
      ui->text_binary->append(
          QString("0x%1").arg(tmp, 8, 16, QLatin1Char('0')));
    }
    simu_kernel::pc = simu_kernel::op;
  }
  catch (const char *error)
  {
    QMessageBox::warning(nullptr, "警告", error);
    return;
  }
}

void Simulator::bin2asm()
{
  QString BinIns = ui->text_binary->toPlainText();
  stringstream out;
  if (BinIns.at(1) == 'x')
  {
    QStringList Bins = BinIns.split("0x");
    for (auto it = Bins.begin() + 1; it != Bins.end(); ++it)
    {
      out << QString("%1 ")
                 .arg(it->toUInt(nullptr, 16), 32, 2, QLatin1Char('0'))
                 .toStdString();
    }
  }
  else if (BinIns.at(1) == 'b')
  {
    QStringList Bins = BinIns.split("0b");
    for (auto it = Bins.begin() + 1; it != Bins.end(); ++it)
    {
      out << it->toStdString() << ' ';
    }
  }
  ui->text_assemble->clear();
  try
  {
    ui->text_assemble->append(QString::fromStdString(Bin2Asm(out.str())));
    uint32_t i = 0;
    if (BinIns.at(1) == 'x')
    {
      QStringList Bins = BinIns.split("0x");
      for (auto it = Bins.begin() + 1; it != Bins.end(); ++it)
      {
        uint32_t tmp = it->toUInt(nullptr, 16);
        simu_kernel::op[i] = tmp;
        ++i;
      }
    }
    else if (BinIns.at(1) == 'b')
    {
      QStringList Bins = BinIns.split("0b");
      for (auto it = Bins.begin() + 1; it != Bins.end(); ++it)
      {
        uint32_t tmp = it->toUInt(nullptr, 2);
        simu_kernel::op[i] = tmp;
        ++i;
      }
    }
    simu_kernel::pc = simu_kernel::op;
  }
  catch (const char *error)
  {
    QMessageBox::warning(nullptr, "警告", error);
  }
}

void Simulator::over() { ui->output->insertPlainText(QString('\n')); }

void Simulator::input()
{
  ui->input->setReadOnly(false);
  ui->input->setPlaceholderText("请填入一个字符");
  if (ui->input->text().isEmpty())
  {
    simu_kernel::in = false;
  }
  else
  {
    simu_kernel::in = true;
    simu_kernel::inout = ui->input->text().at(0).toLatin1();
    ui->input->clear();
    ui->input->setReadOnly(true);
    ui->input->setPlaceholderText("当前不可输入");
  }
}

void Simulator::output()
{
  ui->output->insertPlainText(QString(simu_kernel::inout));
}
